﻿namespace System.Web.Mvc
{
    using System;
    using System.Linq.Expressions;
    using System.Web.Routing;

    /// <summary>
    /// Typed controller action that provides a <c ref="RouteValueDictionary">RouteValueDictionary</c> based on the parameters passed.
    /// </summary>
    /// <typeparam name="TController">The type of the controller.</typeparam>
    public class ControllerAction<TController>
        where TController : IController
    {
        /// <summary>
        /// Initializes a new instance of the ControllerAction class.
        /// </summary>
        /// <param name="action">The controller action.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This behavior is expected. It is wanted to recieve a expression of that type.")]
        public ControllerAction(Expression<Func<TController, ActionResult>> action)
        {
            this.DefaultValues = new RouteValueDictionary();
            this.DefaultValues.Add("controller", typeof(TController).Name.Remove(typeof(TController).Name.LastIndexOf("Controller", StringComparison.OrdinalIgnoreCase)));

            var decorations = (action.Body as MethodCallExpression).Method.GetCustomAttributes(typeof(ActionNameAttribute), true);

            var methodCall = action.Body as MethodCallExpression;

            if (decorations != null && decorations.Length == 1)
            {
                this.DefaultValues.Add("action", (decorations[0] as ActionNameAttribute).Name);
            }
            else
            {
                this.DefaultValues.Add("action", methodCall.Method.Name);
            }

            var paremeters = methodCall.Method.GetParameters();

            for (int parameterIndex = 0; parameterIndex < paremeters.Length; parameterIndex++)
            {
                object value = null;
                var argumentExpression = methodCall.Arguments[parameterIndex];

                var constantArgumentExpression = argumentExpression as ConstantExpression;

                if (constantArgumentExpression != null)
                {
                    value = constantArgumentExpression.Value;

                    this.DefaultValues.Add(
                        paremeters[parameterIndex].Name,
                        value);
                }
            }
        }

        /// <summary>
        /// Gets the default route values.
        /// </summary>
        /// <value>The default route values.</value>
        public RouteValueDictionary DefaultValues
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the controller name from the default values.
        /// </summary>
        /// <value>The controller name.</value>
        public string Controller
        {
            get
            {
                return this.DefaultValues["controller"] as string;
            }
        }

        /// <summary>
        /// Gets the controller action from the default values.
        /// </summary>
        /// <value>The controller action.</value>
        public string Action
        {
            get
            {
                return this.DefaultValues["action"] as string;
            }
        }
    }
}
